package com.ksyun.alert.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ksyun.alert.vo.AlertRecordDetailsVO;
import com.ksyun.alert.customException.SkyAlertException;
import com.ksyun.alert.dto.AlertRulesPageQueryDTO;
import com.ksyun.alert.entity.AlertMetricsDO;
import com.ksyun.alert.entity.AlertRecordsDO;
import com.ksyun.alert.entity.AlertRulesDO;
import com.ksyun.alert.mapper.AlertRulesMapper;
import com.ksyun.alert.result.PageResult;
import com.ksyun.alert.service.AlertMetricsService;
import com.ksyun.alert.service.AlertRecordsService;
import com.ksyun.alert.service.AlertRulesService;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.ksyun.alert.constant.RedisStreamConstant.RULE_IMMEDIATE_KEY;
import static com.ksyun.alert.constant.RedisStreamConstant.RULE_TIMING_KEY;


/**
 * 告警规则表接口实现层
 * @author 蓝
 */
@Service
public class AlertRulesServiceImpl extends ServiceImpl<AlertRulesMapper, AlertRulesDO> implements AlertRulesService {

    @Resource
    private AlertRecordsService alertRecordsService;

    @Resource
    private AlertMetricsService alertMetricsService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public List<AlertRulesDO> getRules() {
        return Stream.concat(this.getImmediateRules().stream(), this.getTimingRules().stream())
                .collect(Collectors.toList());
    }

    @Override
    public List<AlertRulesDO> getImmediateRules() {
        // 先查缓存
        Set<String> members = stringRedisTemplate.opsForSet().members(RULE_IMMEDIATE_KEY);
        if (!CollectionUtils.isEmpty(members)) {
            return members.stream()
                    .map(v -> JSON.parseObject(v, AlertRulesDO.class))
                    .collect(Collectors.toList());
        }
        synchronized (this) {
            Set<String> membersCheck = stringRedisTemplate.opsForSet().members(RULE_IMMEDIATE_KEY);
            if (!CollectionUtils.isEmpty(membersCheck)) {
                return membersCheck.stream()
                        .map(v -> JSON.parseObject(v, AlertRulesDO.class))
                        .collect(Collectors.toList());
            }

            LambdaQueryWrapper<AlertRulesDO> wrapper = Wrappers.lambdaQuery(AlertRulesDO.class)
                    .eq(AlertRulesDO::getType, 0);
            List<AlertRulesDO> immediateRuleList = this.baseMapper.selectList(wrapper);
            stringRedisTemplate.opsForSet().add(RULE_IMMEDIATE_KEY, immediateRuleList.stream()
                    .map(JSON::toJSONString)
                    .toArray(String[]::new));
            return immediateRuleList;
        }
    }

    @Override
    public List<AlertRulesDO> getTimingRules() {
        // 先查缓存
        Set<String> members = stringRedisTemplate.opsForSet().members(RULE_TIMING_KEY);
        if (!CollectionUtils.isEmpty(members)) {
            return members.stream()
                    .map(v -> JSON.parseObject(v, AlertRulesDO.class))
                    .collect(Collectors.toList());
        }
        synchronized (this) {
            Set<String> membersCheck = stringRedisTemplate.opsForSet().members(RULE_TIMING_KEY);
            if (!CollectionUtils.isEmpty(membersCheck)) {
                return membersCheck.stream()
                        .map(v -> JSON.parseObject(v, AlertRulesDO.class))
                        .collect(Collectors.toList());
            }

            LambdaQueryWrapper<AlertRulesDO> wrapper = Wrappers.lambdaQuery(AlertRulesDO.class)
                    .eq(AlertRulesDO::getType, 1);
            List<AlertRulesDO> immediateRuleList = this.baseMapper.selectList(wrapper);
            stringRedisTemplate.opsForSet().add(RULE_TIMING_KEY, immediateRuleList.stream()
                    .map(JSON::toJSONString)
                    .toArray(String[]::new));
            return immediateRuleList;
        }
    }

    @Override
    public boolean insertAlertRule(AlertRulesDO alertRulesDO) {
        boolean save = save(alertRulesDO);
        if (!save) {
            return false;
        }
        String ruleName = alertRulesDO.getType() == 0 ? RULE_IMMEDIATE_KEY : RULE_TIMING_KEY;
        //将全部规则缓存清空，下次来了再查就行
        stringRedisTemplate.delete(ruleName);
        return true;
    }

    @Override
    public PageResult pageAlertRules(AlertRulesPageQueryDTO alertRulesPageQueryDTO) {
        Integer queryPage = alertRulesPageQueryDTO.getPage();
        Integer pageSize = alertRulesPageQueryDTO.getPageSize();
        String type = alertRulesPageQueryDTO.getType();
        Page<AlertRulesDO> page = new Page<>(queryPage, pageSize);
        LambdaQueryWrapper<AlertRulesDO> queryWrapper = new LambdaQueryWrapper<>();
        boolean condition = type == null || type.isEmpty();
        if (!condition) {
            if ("实时计算".equals(type)) {
                queryWrapper.eq(AlertRulesDO::getType, 0);
            }
            if ("定时计算".equals(type)) {
                queryWrapper.eq(AlertRulesDO::getType, 1);
            }
        }
        Page<AlertRulesDO> alertRules = page(page, queryWrapper);
        return new PageResult(alertRules.getTotal(), alertRules.getRecords());
    }

    @Override
    public boolean deleteAlertRule(Long id) {
        //拿到该id的规则
        LambdaQueryWrapper<AlertRulesDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(id != null, AlertRulesDO::getId, id);
        AlertRulesDO rule = getOne(queryWrapper);
        if (rule == null) {
            throw new SkyAlertException("删除规则失败");
        }
        int type = rule.getType();
        //缓存类型
        String cacheName = type == 0 ? RULE_IMMEDIATE_KEY : RULE_TIMING_KEY;
        boolean removeById = removeById(id);
        //干掉缓存，下一次来了再查
        stringRedisTemplate.delete(cacheName);
        return removeById;
    }

    @Override
    public boolean updateAlertRule(AlertRulesDO alertRulesDO) {
        Integer id = alertRulesDO.getId();
        if (id == null) {
            throw new SkyAlertException("告警规则id不能为空！");
        }
        LambdaQueryWrapper<AlertRulesDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AlertRulesDO::getId, id);
        AlertRulesDO alertRule = getOne(queryWrapper);
        //缓存类型
        String cacheName = alertRule.getType() == 0 ? RULE_IMMEDIATE_KEY : RULE_TIMING_KEY;
        stringRedisTemplate.delete(cacheName);
        return updateById(alertRulesDO);
    }

    @Override
    public AlertRecordDetailsVO getAlertRecordDetails(int alertId) {
        AlertRecordsDO alertRecord = alertRecordsService.getById(alertId);
        AlertRulesDO rule = this.getById(alertRecord.getRuleId());
        LambdaQueryWrapper<AlertMetricsDO> wrapper = Wrappers.lambdaQuery(AlertMetricsDO.class)
                                                            .eq(AlertMetricsDO::getRecordId, alertId);
        List<AlertMetricsDO> alertMetricsDOsList = alertMetricsService.getBaseMapper().selectList(wrapper);
        return AlertRecordDetailsVO.builder()
                .alertRuleDesc(rule.getDescription())
                .allExpression(rule.getExpression() + rule.getComparison() + rule.getThreshold())
                .occurrenceTime(rule.getCreateTime().toString())
                .severity(rule.getSeverity())
                .paramsMap(alertMetricsDOsList.stream().collect(Collectors.toMap(AlertMetricsDO::getMetricName, AlertMetricsDO::getValue)))
                .build();
    }
}